/** boost::asio提供了asio::mutable_buffer 和 asio::const_buffer这两个结构，他们是一段连续的空间，首字节存储了后续数据的长度。
 * asio::mutable_buffer用于写服务，asio::const_buffer用于读服务。但是这两个结构都没有被asio的api直接使用。
 *
 * 对于api的buffer参数，asio提出了MutableBufferSequence和ConstBufferSequence概念，他们是由多个asio::mutable_buffer和asio::const_buffer组成的。也就是说boost::asio为了节省空间，将一部分连续的空间组合起来，作为参数交给api使用。
 * 我们可以理解为MutableBufferSequence的数据结构为std::vector<asio::mutable_buffer>
 * 结构如下
 * https://cdn.llfc.club/1676257797218.jpg
 * 每隔vector存储的都是mutable_buffer的地址，每个mutable_buffer的第一个字节表示数据的长度，后面跟着数据内容。
 *
 * 这么复杂的结构交给用户使用并不合适，所以asio提出了buffer()函数，该函数接收多种形式的字节流，
 * 该函数返回asio::mutable_buffers_1 o或者asio::const_buffers_1结构的对象。
 *
 * 如果传递给buffer()的参数是一个只读类型，则函数返回asio::const_buffers_1 类型对象。
 * 如果传递给buffer()的参数是一个可写类型，则返回asio::mutable_buffers_1 类型对象。
 *
 * asio::const_buffers_1和asio::mutable_buffers_1是asio::mutable_buffer和asio::const_buffer的适配器，
 * 提供了符合MutableBufferSequence和ConstBufferSequence概念的接口，所以他们可以作为boost::asio的api函数的参数使用。
 *
 * 简单概括一下，我们可以用buffer()函数生成我们要用的缓存存储数据。
 * 比如boost的发送接口send要求的参数为ConstBufferSequence类型
*/

#include <string>
#include <iostream>

#include <boost/asio.hpp>

namespace asio = boost::asio;
// 学到了 c++命名空间别名

void use_const_bufffer()
{
    std::string str { "Hello, world!" };
    boost::asio::const_buffer asio_const_buffer { str.data(), str.size() };
    std::vector<boost::asio::const_buffer> buffers_sequence;
    buffers_sequence.push_back(asio_const_buffer);
    // boost::send()
}

void use_buffer_string()
{
    std::string str { "Hello, world!" };
    auto buffer = boost::asio::buffer(str);
}

void use_buffer_char_arr()
{
    const size_t BUF_SIZE = 1024;
    std::unique_ptr<char[]> buffer = std::make_unique<char[]>(BUF_SIZE);
    auto input_buffer = asio::buffer(buffer.get(), BUF_SIZE);
    // 这里通过buffer.get()获取底层的原始指针，同时指定缓冲区的大小。
}

void write_to_socket(asio::ip::tcp::socket& socket)
{
    std::string buffer_string { "Hello, world!" };
    size_t bytes_written = 0;

    // 分片发送
    // 在异步的情况下经常需要用到
    while (bytes_written < buffer_string.size())
    {
        bytes_written += socket.write_some(asio::buffer(buffer_string.data() + bytes_written,
                                                        buffer_string.size() - bytes_written));
    }
}

int send_data_by_write_some()
{
    auto io_context = asio::io_context();
    std::string raw_ip_address { "192.168.1.1" };
    unsigned short port_number = 8888;
    try
    {
        asio::ip::tcp::endpoint endpoint { asio::ip::address::from_string(raw_ip_address), port_number };
        asio::ip::tcp::socket socket { io_context, endpoint.protocol() };
        socket.connect(endpoint);

        std::string buffer_string { "Hello, world!" };
        // 一次性全部发送
        size_t sent_bytes1 = socket.send(asio::buffer(buffer_string.data(), buffer_string.size()));
        size_t sent_bytes2 = asio::write(socket, asio::buffer(buffer_string.data(), buffer_string.size()));
    }
    catch (std::exception& e)
    {
        std::cout << e.what() << std::endl;
    }
}

std::string read_from_socket(asio::ip::tcp::socket& socket)
{
    const unsigned int BUF_SIZE = 15;
    std::unique_ptr<char[]> buffer = std::make_unique<char[]>(BUF_SIZE);

    std::size_t bytes_read = 0;
    while (bytes_read < BUF_SIZE)
    {
        bytes_read += socket.read_some(asio::buffer(buffer.get() + bytes_read, BUF_SIZE - bytes_read));
    }

    return std::string(buffer.get(), bytes_read);
}

int main(int argc, char* argv[])
{
}
